package com.xhj.mybatis.scripting.defaults;

import com.xhj.mybatis.builder.SqlSourceBuilder;
import com.xhj.mybatis.mapping.BoundSql;
import com.xhj.mybatis.mapping.SqlSource;
import com.xhj.mybatis.scripting.xmltags.DynamicContext;
import com.xhj.mybatis.scripting.xmltags.SqlNode;
import com.xhj.mybatis.session.Configuration;

import java.util.HashMap;

/**
 * RawSqlSource
 *
 * @author XJks
 * @description RawSqlSource 表示静态 SQL 语句的 SqlSource 实现。
 * <p>
 * TODO:
 *  1.RawSqlSource 适用于静态 SQL（即 SQL 语句在编译时就能确定，没有动态标签<if>，<foreach>等）。
 *  2.构造时会将 SqlNode 解析为 SQL 字符串，并通过 SqlSourceBuilder 解析参数占位符，生成最终的 SqlSource 实例（通常是 StaticSqlSource）。性能较高，因为解析工作在启动时完成。
 *  3.内部持有一个 StaticSqlSource实例，实际用于生成 BoundSql 对象。
 */
public class RawSqlSource implements SqlSource {

    /**
     * 持有一个 SqlSource 实例，实际用于生成 BoundSql
     */
    private final SqlSource sqlSource;

    /**
     * 构造方法，接收 Configuration、SqlNode 和参数类型。
     * 通过 getSql 方法将 SqlNode 转换为 SQL 字符串，再调用另一个构造方法。
     *
     * @param configuration MyBatis 配置对象
     * @param rootSqlNode   SQL 节点树的根节点
     * @param parameterType 参数类型
     */
    public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
        this(configuration, getSql(configuration, rootSqlNode), parameterType);
    }

    /**
     * 构造方法，接收 Configuration、SQL 字符串和参数类型。
     * 解析 SQL 并生成 SqlSource 实例。
     *
     * @param configuration MyBatis 配置对象
     * @param sql           SQL 语句字符串
     * @param parameterType 参数类型
     */
    public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
        // 创建 SqlSourceBuilder，用于解析 SQL
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        // 如果参数类型为 null，默认使用 Object.class
        Class<?> clazz = parameterType == null ? Object.class : parameterType;
        // 解析 SQL，生成 SqlSource 实例，存入成员变量
        sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
    }

    /**
     * 将 SqlNode 转换为 SQL 字符串。
     *
     * @param configuration MyBatis 配置对象
     * @param rootSqlNode   SQL 节点树的根节点
     * @return 解析后的 SQL 字符串
     */
    private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
        // 创建动态上下文对象
        DynamicContext context = new DynamicContext(configuration, null);
        // 应用 SqlNode，将 SQL 内容写入 context
        rootSqlNode.apply(context);
        // 返回最终生成的 SQL 字符串
        return context.getSql();
    }

    /**
     * 获取 BoundSql 对象，包含最终 SQL 和参数信息。
     *
     * @param parameterObject 参数对象
     * @return BoundSql 实例
     */
    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        // 委托给内部的 sqlSource 实现
        return sqlSource.getBoundSql(parameterObject);
    }

}
